package com.atom.plugin;

import com.android.build.api.transform.DirectoryInput;
import com.android.build.api.transform.JarInput;
import com.android.build.api.transform.Status;
import com.android.build.api.transform.TransformInvocation;
import com.atom.plugin.core.AbstractPlugin;
import com.atom.plugin.core.Log;

import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.io.FileUtils;
import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;

import org.objectweb.asm.*;
import org.objectweb.asm.tree.ClassNode;

import kotlin.Unit;
import kotlin.jvm.functions.Function1;

public class AapPlugin extends AbstractPlugin<AapExtension> {
    public static final String NAME = "ApiImplRouter";
    public static final String GENERATE_TO_CLASS_NAME = "com/atom/core/ApiImplRouter";
    public static final String GENERATE_TO_CLASS_FILE_NAME = GENERATE_TO_CLASS_NAME + ".class";
    public static final String GENERATE_TO_METHOD_NAME = "loadProxyClass";
    public static final String ROUTER_CLASS_PACKAGE_NAME = "com/atom/apt/proxy";
    public static final String REGISTER_METHOD_NAME = "registerClass";

    public List<String> scanningResultList = new ArrayList<>();
    public File scanningInsertResultFileClass = null;
    public boolean leftSlash = File.separator.equals("/");

    @NotNull
    @Override
    public Class<AapExtension> getExtensionClass() {
        return AapExtension.class;
    }

    @NotNull
    @Override
    public String getExtensionName() {
        return this.getClass().getSimpleName();
    }

    @Override
    public boolean isFilterDir(@NotNull DirectoryInput dirFile) {
        Log.INSTANCE.e("过滤 isFilterDir " + dirFile.getName() + "---" + dirFile.getFile().getAbsolutePath());
        return super.isFilterDir(dirFile);
    }

    @NotNull
    @Override
    public byte[] transformDir(@NotNull byte[] bytes, @NotNull File inputFile, @NotNull File outputFile) {
        String absolutePath = inputFile.getAbsolutePath();
        if (!leftSlash) {
            absolutePath = absolutePath.replaceAll("\\\\", "/");
        }
        if (inputFile.isFile() && absolutePath.contains(ROUTER_CLASS_PACKAGE_NAME)) {
            Log.INSTANCE.e("transformDir 找到指定的需要搜集的类 " + inputFile.getName());
            return scanning(bytes);
        }
        return bytes;
    }

    @Override
    public boolean isFilterJar(@NotNull JarInput jarFile) {
        Log.INSTANCE.e("过滤 isFilterJar " + jarFile.getName() + "---" + jarFile.getFile().getAbsolutePath());
        final String path = jarFile.getFile().getAbsolutePath();
        if (path.contains("atom-core")) {
            return super.isFilterJar(jarFile);
        }
        for (String s : extension.getFilterJar()) {
            if (path.contains(s)) {
                return true;
            }
        }
        return super.isFilterJar(jarFile);
    }


    @Override
    protected void print(boolean isJar, @NotNull Status status, @NotNull String inputFile, @NotNull String outputFile) {
        super.print(isJar, status, inputFile, outputFile);
    }

    @NotNull
    @Override
    public byte[] transformJar(@NotNull byte[] bytes, @NotNull JarEntry entry, @NotNull File inputFile, @NotNull File outputFile) {
        if (GENERATE_TO_CLASS_FILE_NAME.equals(entry.getName())) {
            Log.INSTANCE.e("transformJar 找到指定的需要插入的ApiImpl类的 " + entry.getName());
            scanningInsertResultFileClass = outputFile;
        } else if (entry.getName().startsWith(ROUTER_CLASS_PACKAGE_NAME)) {
            Log.INSTANCE.e("transformJar 找到指定的需要搜集的类 " + entry.getName());
            return scanning(bytes);
        }
        return bytes;
    }

    private byte[] scanning(byte[] inputArray) {
        ClassReader reader = new ClassReader(inputArray);
        ClassNode node = new ClassNode();
        reader.accept(node, ClassReader.EXPAND_FRAMES);
        for (AapScanner entry : extension.registerList) {
            if (entry.isInterface()) {
                for (String interfaceName : reader.getInterfaces()) {
                    if (entry.name.equals(interfaceName)) {
                        Log.INSTANCE.e(String.format("transformJar 找到一个 interfaceClass [%s]的类 <|> %s <|> %s"
                                , interfaceName, entry.name, reader.getClassName()));
                        scanningResultList.add(reader.getClassName());
                    }
                }

            } else {
                if (entry.name.equals(reader.getSuperName())) {
                    Log.INSTANCE.e(String.format("transformJar 找到一个 superClass [%s]的类 <|> %s <|> %s",
                            reader.getSuperName(), entry.name, reader.getClassName()));
                    scanningResultList.add(reader.getClassName());
                }
            }
        }
        ClassWriter writer = new ClassWriter(1);
        node.accept(writer);
        return writer.toByteArray();
    }

    @Override
    protected void onFinishTransform(@NotNull TransformInvocation transformInvocation) {
        if (scanningInsertResultFileClass == null) return;
        if (!scanningInsertResultFileClass.exists()) return;
        if (scanningResultList.isEmpty()) return;
        if (scanningInsertResultFileClass.getName().endsWith(".jar")) {
            File optJar = new File(scanningInsertResultFileClass.getParent(),
                    scanningInsertResultFileClass.getName() + ".opt");
            if (optJar.exists()) {
                try {
                    FileUtils.forceDelete(optJar);
                } catch (IOException e) {
                    e.printStackTrace();
                    Log.INSTANCE.fatal(e);
                }
            }
            try {
                JarFile file = new JarFile(scanningInsertResultFileClass);
                Enumeration<JarEntry> enumeration = file.entries();
                JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(optJar));
                //遍历jar包，找到 LogisticsCenter.class 文件
                while (enumeration.hasMoreElements()) {
                    JarEntry jarEntry = enumeration.nextElement();
                    String entryName = jarEntry.getName();
                    ZipEntry zipEntry = new ZipEntry(entryName);
                    InputStream inputStream = file.getInputStream(jarEntry);
                    jarOutputStream.putNextEntry(zipEntry);
                    if (GENERATE_TO_CLASS_FILE_NAME.equals(entryName)) {
                        Log.INSTANCE.e("afterEvaluate Insert init code to class = " + entryName);
                        byte[] bytes = referHackWhenInit(inputStream);
                        jarOutputStream.write(bytes);
                    } else {
                        jarOutputStream.write(IOUtils.toByteArray(inputStream));
                    }
                    inputStream.close();
                    jarOutputStream.closeEntry();
                }
                jarOutputStream.close();
                file.close();
                if (scanningInsertResultFileClass.exists()) {
                    if (scanningInsertResultFileClass.delete()) {
                        optJar.renameTo(scanningInsertResultFileClass);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
                Log.INSTANCE.fatal(e);
            }
        }
    }


    private byte[] referHackWhenInit(InputStream inputStream) throws IOException {
        ClassReader cr = new ClassReader(inputStream);
        ClassWriter cw = new ClassWriter(cr, 0);
        AapImplClassVisitor cv = new AapImplClassVisitor(Opcodes.ASM5, cw);
        cr.accept(cv, ClassReader.EXPAND_FRAMES);
        return cw.toByteArray();
    }

    class AapImplClassVisitor extends ClassVisitor {
        public AapImplClassVisitor(int api, ClassVisitor classVisitor) {
            super(api, classVisitor);
        }

        @Override
        public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
            MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
            //generate code into this method
            if (GENERATE_TO_METHOD_NAME.equals(name)) { //找到动态生成注册代码需要注入的 loadRouterMap 方法
                mv = new AapImplMethodVisitor(Opcodes.ASM5, mv);
            }
            return mv;
        }
    }

    class AapImplMethodVisitor extends MethodVisitor {

        public AapImplMethodVisitor(int api, MethodVisitor methodVisitor) {
            super(api, methodVisitor);
        }

        @Override
        public void visitInsn(int opcode) {
            //generate code before return
            if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)) {
                for (String name : scanningResultList) {
                    name = name.replaceAll("/", "."); //将类文件的路径转化为包的路径
                    Log.INSTANCE.e("transformJar AapImplMethodVisitor visitInsn <> " + name);
                    mv.visitLdcInsn(name); //访问方法的参数--搜索到的接口类名
                    // 生成注册代码到 LogisticsCenter.loadRouterMap() 方法中
                    mv.visitMethodInsn(Opcodes.INVOKESTATIC   //操作码
                            , GENERATE_TO_CLASS_NAME //访问类的类名
                            , REGISTER_METHOD_NAME //访问的方法
                            , "(Ljava/lang/String;)V"   //访问参数的类型
                            , false); //访问的类是否是接口
                }
            }

            super.visitInsn(opcode);
        }

    }

}
